/** @file
MCU information parsing and retrieval interfaces.

Copyright (C) 2022 - 2023, Phytium Technology Co., Ltd. All rights reserved.<BR>

SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#include "McuInfo.h"
#include <Library/HobLib.h>
#include <Library/ScmiLib.h>
#include <PhytiumSmbiosHelper.h>
#include <IndustryStandard/SmBios.h>
#include <Library/ArmSmcLib.h>

#define RDIMM_TYPE                1
#define UDIMM_TYPE                2
#define SODIMM_TYPE               3
#define LRDIMM_TYPE               4

#define SCMI_PROTOCOL_ID_DDR      0x14
#define SCMI_MESSAGE_ID_DDR       0x6
/**
  Calculate the power of number B with base number A as the exponent.

  @param[in]  NumberA  The base of the formula.
  @param[in]  NumberB  The exponent of the formula.

  @retval  Power      Reserved.

**/
UINT32
Pow (
  INT8 NumberA,
  INT8 NumberB
  )
{
  if (NumberB == 0)
    return 1;
  else
    return NumberA * Pow (NumberA, NumberB - 1);
}

/**
  Fix Ddr speed.

  @param[in]  Speed  Ddr Speed to be fix.

  @retval     RealSpeed.
**/
UINT16
FixDdrSpeed (
  UINT16 Speed
  )
{
  UINT16 RealSpeed;

  RealSpeed = 0;
  switch (Speed) {
    case 1868:
      RealSpeed = 1866;
      break;

    case 2132:
      RealSpeed = 2133;
      break;

    case 2668:
      RealSpeed = 2666;
      break;

    case 2932:
      RealSpeed = 2933;
      break;

    default:
      RealSpeed = Speed;
  }

  return RealSpeed;
}

/**
  Get Ddr current speed.

  @retval     Ddr current Speed.
**/
UINT32
GetDdrCurrentSpeed (
  VOID
  )
{
  UINT32             PayLoad[3];
  UINT32             Len;
  UINT32             ClockId;
  UINT32             Freq;
  EFI_STATUS         Status;
  SCMI_ADDRESS_BASE  Base;

  Base.MhuConfigBase = PcdGet64 (PcdMhuConfigBaseAddress);
  Base.MhuBase = PcdGet64 (PcdMhuBaseAddress);
  Base.ShareMemoryBase = PcdGet64 (PcdMhuShareMemoryBase);
  ClockId = 0x6;
  Status = ScmiCommandExecute (
             &Base,
             SCMI_PROTOCOL_ID_DDR,
             SCMI_MESSAGE_ID_DDR,
             1,
             &ClockId,
             &Len,
             PayLoad
             );
  if (EFI_ERROR (Status)) {
    return 0x0;
  }

  Freq = PayLoad[1] / 1000000;

  return Freq;
}


/**
  Print Ddr information.

  @param[in]  DimmIndex        Dimm Index.
  @param[in]  DdrConfigData    A pointer to PARAMETER_DDR_CONFIG_V3.
  @param[out] SmbiosMemoryInfo A pointer to PHYTIUM_MEMORY_SMBIOS_INFO.

**/
VOID
PrintDdrInfo (
  IN  UINT8                       DimmIndex,
  IN  PARAMETER_DDR_CONFIG_V3     *DdrConfigData,
  OUT PHYTIUM_MEMORY_SMBIOS_INFO  *SmbiosMemoryInfo
  )
{
#if 1
  UINT8               Temp;
  UINT16              SdramDensity;
  UINT16              RealSpeed;
  UINT64              DimmCapacity;
  MEMORY_DEVICE_INFO  *MemoryDeviceInfo;

  MemoryDeviceInfo = &SmbiosMemoryInfo->MemoryDevice[DimmIndex];
  if (DdrConfigData->ChannelInfo.DramType == DDR4_TYPE){
    DEBUG ((DEBUG_INFO, "  tAAmim    = %dps\n", DdrConfigData->ChannelInfo.TAAmin));
    DEBUG ((DEBUG_INFO, "  tRCDmin   = %dps\n", DdrConfigData->ChannelInfo.TRCDmin));
    DEBUG ((DEBUG_INFO, "  tRPmin    = %dps\n", DdrConfigData->ChannelInfo.TRPmin));
    DEBUG ((DEBUG_INFO, "  tRASmin   = %dps\n", DdrConfigData->ChannelInfo.TRASmin));
    DEBUG ((DEBUG_INFO, "  tRCmin    = %dps\n", DdrConfigData->ChannelInfo.TRCmin));
    DEBUG ((DEBUG_INFO, "  tFAWmin   = %dps\n", DdrConfigData->ChannelInfo.TFAWmin));
    DEBUG ((DEBUG_INFO, "  tRRD_Smin = %dps\n", DdrConfigData->ChannelInfo.TRRDSmin));
    DEBUG ((DEBUG_INFO, "  tRRD_Lmin = %dps\n", DdrConfigData->ChannelInfo.TRRDLmin));
    DEBUG ((DEBUG_INFO, "  tCCD_Lmin = %dps\n", DdrConfigData->ChannelInfo.TCCDLmin));
    DEBUG ((DEBUG_INFO, "  tWRmin    = %dps\n", DdrConfigData->ChannelInfo.TWRmin));
    DEBUG ((DEBUG_INFO, "  tWTR_Smin = %dps\n", DdrConfigData->ChannelInfo.TWTRSmin));
    DEBUG ((DEBUG_INFO, "  tWTR_Lmin = %dps\n", DdrConfigData->ChannelInfo.TWTRLmin));
    DEBUG ((DEBUG_INFO, "  tRFC1min  = %dps\n", DdrConfigData->ChannelInfo.TRFC1min));
  } else if (DdrConfigData->ChannelInfo.DramType == DDR5_TYPE) {
    DEBUG ((DEBUG_INFO, "  tAAmim    = %dps\n", DdrConfigData->ChannelInfo.TAAmin));
    DEBUG ((DEBUG_INFO, "  tRCDmin   = %dps\n", DdrConfigData->ChannelInfo.TRCDmin));
    DEBUG ((DEBUG_INFO, "  tRPmin    = %dps\n", DdrConfigData->ChannelInfo.TRPmin));
    DEBUG ((DEBUG_INFO, "  tRASmin   = %dps\n", DdrConfigData->ChannelInfo.TRASmin));
    DEBUG ((DEBUG_INFO, "  tRCmin    = %dps\n", DdrConfigData->ChannelInfo.TRCmin));
    DEBUG ((DEBUG_INFO, "  tWRmin    = %dps\n", DdrConfigData->ChannelInfo.TWRmin));
    DEBUG ((DEBUG_INFO, "  tRFC1min  = %dns\n", DdrConfigData->ChannelInfo.TRFC1min));
    DEBUG ((DEBUG_INFO, "  tRFC2min  = %dns\n", DdrConfigData->ChannelInfo.TRFC2min));
    DEBUG ((DEBUG_INFO, "  tRFCsbmin = %dns\n", DdrConfigData->ChannelInfo.TRFC4RFCsbmin));
  } else if (DdrConfigData->ChannelInfo.DramType == LPDDR4_TYPE) {
  }

  Temp = 4 << DdrConfigData->ChannelInfo.DataWidth;
  if (DdrConfigData->ChannelInfo.DramType == DDR3_TYPE) {
    SdramDensity = (1UL << (DdrConfigData->ChannelInfo.RowNum + DdrConfigData->ChannelInfo.ColumnNum)) \
                   * Temp * DdrConfigData->ChannelInfo.BankNum >> 20; //units: Mb
  } else {
    if (DdrConfigData->ChannelInfo.DramType == LPDDR4_TYPE) {
      SdramDensity = (1UL << (DdrConfigData->ChannelInfo.RowNum + DdrConfigData->ChannelInfo.ColumnNum)) \
                     * Temp * DdrConfigData->ChannelInfo.BankNum >> 20; //units: Mb
    } else {
      SdramDensity = (1UL << (DdrConfigData->ChannelInfo.RowNum + DdrConfigData->ChannelInfo.ColumnNum)) \
                     * Temp * DdrConfigData->ChannelInfo.BankGroupNum * DdrConfigData->ChannelInfo.BankNum >> 20; //units: Mb
    }
  }

  if (DdrConfigData->McuConfig.Bits.HalfBitWidth) {
    DimmCapacity = SdramDensity * DdrConfigData->ChannelInfo.RankNum * 32 / Temp >> 13;   //units: GB
  } else {
    DimmCapacity = SdramDensity * DdrConfigData->ChannelInfo.RankNum * 64 / Temp >> 13;   //units: GB
  }
  DEBUG ((DEBUG_INFO, "\tDimm_Capacity = %lldGB\n", DimmCapacity));
  switch (DdrConfigData->ChannelInfo.DramType) {
  case DDR3_TYPE:
    DEBUG ((DEBUG_INFO, "\tDDR3"));
    break;
  case DDR4_TYPE:
    DEBUG ((DEBUG_INFO, "\tDDR4"));
    break;
  case LPDDR4_TYPE:
    DEBUG ((DEBUG_INFO, "\tLPDDR4"));
    break;
  case DDR5_TYPE:
    DEBUG ((DEBUG_INFO, "\tDDR5"));
    break;
  default:
    DEBUG ((DEBUG_INFO, "\tdram_type=0x%x", DdrConfigData->ChannelInfo.DramType));
    break;
  }
  switch (DdrConfigData->ChannelInfo.DimmType) {
  case 1:
    DEBUG ((DEBUG_INFO, "\tRDIMM"));
    break;
  case 2:
    DEBUG ((DEBUG_INFO, "\tUDIMM"));
    break;
  case 3:
  case 9:
    DEBUG ((DEBUG_INFO, "\tSODIMM"));
    break;
  case 4:
    DEBUG ((DEBUG_INFO, "\tLRDIMM"));
    break;
  default:
    DEBUG ((DEBUG_INFO, "\tdimm_type=0x%x", DdrConfigData->ChannelInfo.DimmType));
    break;
  }
  if (DdrConfigData->ChannelInfo.DramType == DDR4_TYPE) {
    DEBUG ((DEBUG_INFO, "/%d Bank Groups", DdrConfigData->ChannelInfo.BankGroupNum));
    DEBUG ((DEBUG_INFO, "/%d Banks", DdrConfigData->ChannelInfo.BankNum));
  } else {
    DEBUG ((DEBUG_INFO, "/%d Bank Groups", DdrConfigData->ChannelInfo.BankGroupNum));
    DEBUG ((DEBUG_INFO, "/%d Banks", DdrConfigData->ChannelInfo.BankNum));
    DEBUG ((DEBUG_INFO, "/sdram_density=%dGb", SdramDensity >> 10));
  }
  DEBUG ((DEBUG_INFO, "/Column %d", DdrConfigData->ChannelInfo.ColumnNum));
  DEBUG ((DEBUG_INFO, "/Row %d", DdrConfigData->ChannelInfo.RowNum));
  switch (DdrConfigData->ChannelInfo.DataWidth) {
  case DIMM_X4:
    DEBUG ((DEBUG_INFO, "/X4"));
    break;
  case DIMM_X8:
    DEBUG ((DEBUG_INFO, "/X8"));
    break;
  case DIMM_X16:
    DEBUG ((DEBUG_INFO, "/X16"));
    break;
  case DIMM_X32:
    DEBUG ((DEBUG_INFO, "/X32"));
    break;
  default:
    DEBUG ((DEBUG_INFO, "/data_width=0x%x", DdrConfigData->ChannelInfo.DataWidth));
    break;
  }
  DEBUG ((DEBUG_INFO, "/%d Rank", DdrConfigData->ChannelInfo.RankNum));
  switch (DdrConfigData->ChannelInfo.EccType) {
  case 0:
    DEBUG ((DEBUG_INFO, "/NO ECC"));
    break;
  case 1:
    DEBUG ((DEBUG_INFO, "/ECC"));
    break;
  default:
    DEBUG ((DEBUG_INFO, "/ecc_type=0x%x", DdrConfigData->ChannelInfo.EccType));
    break;
  }
  if(DdrConfigData->ChannelInfo.MirrorType == 0) {
    DEBUG ((DEBUG_INFO, "/Standard\n"));
  } else {
    DEBUG ((DEBUG_INFO, "/Mirror\n"));
  }
  DEBUG ((DEBUG_INFO, "\tModual:"));
  switch (DdrConfigData->ChannelInfo.ModuleManId) {
  case SAMSUNG_VENDOR:
    DEBUG ((DEBUG_INFO, "Samsung"));
    break;
  case MICRON_VENDOR:
    DEBUG ((DEBUG_INFO, "Micron"));
    break;
  case HYNIX_VENDOR:
    DEBUG ((DEBUG_INFO, "Hynix"));
    break;
  case KINGSTON_VENDOR:
    DEBUG ((DEBUG_INFO, "KingSton"));
    break;
  case RAMAXEL_VENDOR:
    DEBUG ((DEBUG_INFO, "Ramaxel"));
    break;
  case LANQI_VENDOR:
    DEBUG ((DEBUG_INFO, "Lanqi"));
    break;
  case UNILC_VENDOR:
    DEBUG ((DEBUG_INFO, "Unilc"));
    break;
  default:
    DEBUG ((DEBUG_INFO, "Unknown=0x%x", DdrConfigData->ChannelInfo.ModuleManId));
  }
  MemoryDeviceInfo->Size = DimmCapacity;
  RealSpeed = (UINT16)(GetDdrCurrentSpeed ()  << 2);
  RealSpeed = FixDdrSpeed (RealSpeed);
  MemoryDeviceInfo->ConfiguredMemoryClockSpeed = RealSpeed;

#endif
}


/**
  Parse DDR4 Spd information.

  @param[in]  DimmIndex        Dimm Index.
  @param[in]  Buffer           Spd information.
  @param[in]  DdrConfigData    A pointer to PARAMETER_DDR_CONFIG_V3.
  @param[out] SmbiosMemoryInfo A pointer to PHYTIUM_MEMORY_SMBIOS_INFO.

**/
VOID
ParseDDR4Spd (
  IN  UINT8                       DimmIndex,
  IN  UINT8                       *Buffer,
  IN  PARAMETER_DDR_CONFIG_V3     *DdrConfigData,
  OUT PHYTIUM_MEMORY_SMBIOS_INFO  *SmbiosMemoryInfo
  )
{
  MEMORY_DEVICE_INFO  *MemoryDeviceInfo;
  UINT32              SerialNumber;
  UINT16              Temp;

  DEBUG ((DEBUG_INFO, "Parse DDR4 Spd \n"));

  MemoryDeviceInfo = &SmbiosMemoryInfo->MemoryDevice[DimmIndex];
  Temp = Buffer[3] & 0xf;
  DdrConfigData->ChannelInfo.DimmType = Temp;

  //
  //SDRAM Density and Banks
  //
  Temp = Buffer[4] >> 6 & 0x3;
  if (!Temp) {
    DdrConfigData->ChannelInfo.BankGroupNum = 0x0;
  } else {
    DdrConfigData->ChannelInfo.BankGroupNum = 0x1 << Temp;
  }

  Temp = Buffer[4] >> 4 & 0x3;
  if (Temp == 1) {
    DdrConfigData->ChannelInfo.BankNum = 8;
  } else {
    DdrConfigData->ChannelInfo.BankNum = 4;
  }

  //
  //SDRAM Addressing
  //
  Temp = Buffer[5] & 0x7;
  DdrConfigData->ChannelInfo.ColumnNum = Temp + 9;

  Temp = Buffer[5]>>3 & 0x7;
  DdrConfigData->ChannelInfo.RowNum = Temp + 12;

  //Module Organization
  Temp = Buffer[12] & 0x7;
  DdrConfigData->ChannelInfo.DataWidth = Temp;

  Temp = Buffer[12]>>3 & 0x7;
  DdrConfigData->ChannelInfo.RankNum= Temp + 1;

  //
  //Module Organization
  //
  Temp = Buffer[13]>>3 & 0x7;
  DdrConfigData->ChannelInfo.EccType = Temp;

  //
  //(Registered): Address Mapping from Register to DRAM
  //
  if ((DdrConfigData->ChannelInfo.DimmType == RDIMM_TYPE)
        || (DdrConfigData->ChannelInfo.DimmType == LRDIMM_TYPE)) {
    Temp = Buffer[136] & 0x1;
  } else {
    Temp = Buffer[131] & 0x1;
  }

  DdrConfigData->ChannelInfo.MirrorType = Temp;

  //DdrConfigData->ChannelInfo.F0RC03 = (Buffer[137]>>4) & MASK_4BIT;
  //DdrConfigData->ChannelInfo.F0RC04 = Buffer[137] & MASK_4BIT;
  //DdrConfigData->ChannelInfo.F0RC05 = Buffer[138] & MASK_4BIT;

  DdrConfigData->ChannelInfo.TRFC1min = Buffer[31];
  DdrConfigData->ChannelInfo.TRFC2min = Buffer[33];
  DdrConfigData->ChannelInfo.TRFC4RFCsbmin = Buffer[35];

  //DdrConfigData->ChannelInfo.RcdNum = (UINT16)Buffer[256]; lanqi
  DdrConfigData->ChannelInfo.ModuleManId = ((UINT16)Buffer[320] << 8) + Buffer[321];

  //tAAmin , Buffer[123] may be positive/negative
  DdrConfigData->ChannelInfo.TAAmin = Buffer[24] * SPD_MTB + (INT8)Buffer[123] * SPD_FTB;
  //tRCDmin , Buffer[122] may be positive/negative
  DdrConfigData->ChannelInfo.TRCDmin = Buffer[25] * SPD_MTB + (INT8)Buffer[122] * SPD_FTB;
  //tRPmin
  DdrConfigData->ChannelInfo.TRPmin = Buffer[26] * SPD_MTB + (INT8)Buffer[121] * SPD_FTB;
  //tRASmin
  DdrConfigData->ChannelInfo.TRASmin = (((((UINT16)Buffer[27]) & 0xf)<<8) + Buffer[28]) * SPD_MTB;
  //tRCmin
  DdrConfigData->ChannelInfo.TRCmin = (((((UINT16)Buffer[27]>>4) & 0xf)<<8) + Buffer[29]) * SPD_MTB + (INT8)Buffer[120] * SPD_FTB;
  //tFAWmin
  DdrConfigData->ChannelInfo.TFAWmin = ((((UINT16)Buffer[36] & 0xf) <<8) + Buffer[37]) * SPD_MTB;
  //tRDD_Smin
  DdrConfigData->ChannelInfo.TRRDSmin = Buffer[38] * SPD_MTB + (INT8)Buffer[119] * SPD_FTB;
  //tRDD_Lmin
  DdrConfigData->ChannelInfo.TRRDLmin = Buffer[39] * SPD_MTB + (INT8)Buffer[118] * SPD_FTB;
  //tCCD_Lmin
  DdrConfigData->ChannelInfo.TCCDLmin = Buffer[40] * SPD_MTB + (INT8)Buffer[117] * SPD_FTB;
  //tWRmin
  if ((Buffer[42] == 0x0) && (Buffer[41] == 0x0) ) {
    DEBUG ((DEBUG_INFO, "Error! spd byte42 = 0\n"));
    DdrConfigData->ChannelInfo.TWRmin = 15000;
  } else {
    DdrConfigData->ChannelInfo.TWRmin = Buffer[41] * SPD_MTB * 256 + Buffer[42] * SPD_MTB;
  }

  if ((Buffer[43] == 0) && (Buffer[44] == 0) ) {
    DEBUG ((DEBUG_INFO, "Error! spd byte43 = 0\n"));
    DdrConfigData->ChannelInfo.TWTRSmin = 2500;
  } else {
    DdrConfigData->ChannelInfo.TWTRSmin = ((((UINT16)Buffer[43] & 0xf) << 8) + Buffer[44]) * SPD_MTB;
  }

  if((Buffer[43] == 0) && (Buffer[45] == 0)) {
    DdrConfigData->ChannelInfo.TWTRLmin = 7500;
  } else {
    DdrConfigData->ChannelInfo.TWTRLmin = ((((UINT16)Buffer[43] >> 4) & 0xf) + Buffer[45]) * SPD_MTB;
  }

  //tRFC1min
  DdrConfigData->ChannelInfo.TRFC1min = (((UINT16)Buffer[31]<<8) + Buffer[30]) * SPD_MTB;
  //tRFC2min
  DdrConfigData->ChannelInfo.TRFC2min = (((UINT16)Buffer[33]<<8) + Buffer[32]) * SPD_MTB;
  //tRFC4min
  DdrConfigData->ChannelInfo.TRFC4RFCsbmin = (((UINT16)Buffer[35]<<8) + Buffer[34]) * SPD_MTB;

  PrintDdrInfo (DimmIndex, DdrConfigData, SmbiosMemoryInfo);
  Temp = ((UINT16)Buffer[350] << 8) + Buffer[351];
  DEBUG ((DEBUG_INFO, "/Dram:"));

  switch (Temp) {
  case SAMSUNG_VENDOR:
    DEBUG ((DEBUG_INFO, "Samsung"));
    break;
  case MICRON_VENDOR:
    DEBUG ((DEBUG_INFO, "Micron"));
    break;
  case HYNIX_VENDOR:
    DEBUG ((DEBUG_INFO, "Hynix"));
    break;
  case KINGSTON_VENDOR:
    DEBUG ((DEBUG_INFO, "KingSton"));
    break;
  case RAMAXEL_VENDOR:
    DEBUG ((DEBUG_INFO, "Ramaxel"));
    break;
  case LANQI_VENDOR:
    DEBUG ((DEBUG_INFO, "Lanqi"));
    break;
  case CXMT_VENDOR:
    DEBUG ((DEBUG_INFO, "CXMT"));
    break;
  case UNILC_VENDOR:
    DEBUG ((DEBUG_INFO, "Unilc"));
    break;
  default:
    DEBUG ((DEBUG_INFO, "Unknown=0x%x", Temp));
  }

  //Fill Memory smbios information

  //Data Width
  if (DdrConfigData->ChannelInfo.EccType == 1) {
    MemoryDeviceInfo->TotalWidth = 72;
    MemoryDeviceInfo->DataWidth = 64;
  } else {
    MemoryDeviceInfo->TotalWidth = 64;
    MemoryDeviceInfo->DataWidth = 64;
  }

  //Dimm Type
  MemoryDeviceInfo->DimmType = DdrConfigData->ChannelInfo.DimmType;

  //Memory Type
  MemoryDeviceInfo->MemoryType = MemoryTypeDdr4;

  //MaxSpeed
  Temp = Buffer[18] *125 + (INT8)Buffer[125];
  Temp = (1000000 / Temp) * 2;
  MemoryDeviceInfo->MaxSpeed = FixDdrSpeed (Temp);

  //Manufacture
  Temp = ((UINT16)Buffer[350] << 8) + Buffer[351];
  MemoryDeviceInfo->Manufacturer = Temp;

  //SN
  SerialNumber = ((UINT32)Buffer[325] << 24) + ((UINT32)Buffer[326] <<16)
                   + ((UINT32)Buffer[327] << 8) + Buffer[328];
  MemoryDeviceInfo->SerialNumber = SerialNumber;
  DEBUG ((DEBUG_INFO, "/Serial:0x%x\n", SerialNumber));

  //PN
  CopyMem (MemoryDeviceInfo->PartNumber, &Buffer[329], 20);
  MemoryDeviceInfo->PartNumber[20] = '\0';

  //RankNumber
  MemoryDeviceInfo->RankNumber = DdrConfigData->ChannelInfo.RankNum;

  //MemoryTechnology
  MemoryDeviceInfo->MemoryTechnology = MemoryTechnologyDram;


  //ModuleManufacturerID
  Temp = ((UINT16)Buffer[350] << 8) + Buffer[351];
  MemoryDeviceInfo->ModuleManufacturerID = Temp;
}

/**
  Parse DDR5 Spd information.

  @param[in]      DimmIndex         Dimm Index.
  @param[in]      SpdInfo           Spd information.
  @param[in,out]  DdrConfigData     Ddr config data struct.
  @param[out]     SmbiosMemoryInfo  A pointer to PHYTIUM_MEMORY_SMBIOS_INFO.

**/
VOID
ParseDDR5Spd (
  IN UINT8                        DimmIndex,
  IN UINT8                        *SpdInfo,
  IN OUT PARAMETER_DDR_CONFIG_V3  *DdrConfigData,
  OUT PHYTIUM_MEMORY_SMBIOS_INFO  *SmbiosMemoryInfo
  )
{
  MEMORY_DEVICE_INFO  *MemoryDeviceInfo;
  UINT32              SerialNumber;
  UINT16              Temp;

  DEBUG ((DEBUG_INFO, "Parse DDR5 Spd\n"));

  MemoryDeviceInfo = &SmbiosMemoryInfo->MemoryDevice[DimmIndex];

  DdrConfigData->ChannelInfo.DramType   = SpdInfo[2];//waimian
  DdrConfigData->ChannelInfo.DimmType   = SpdInfo[3] & 0xf;
  DdrConfigData->ChannelInfo.RowNum     = ((SpdInfo[5] & 0x1f) + 16);
  DdrConfigData->ChannelInfo.ColumnNum  = (((SpdInfo[5] >> 5) & 0x7) + 10);
  DdrConfigData->ChannelInfo.DataWidth  = ((SpdInfo[6] >> 5) & 0x7);
  DdrConfigData->ChannelInfo.RankNum    = (((SpdInfo[234] >> 3) & 0x7) + 1);
  DdrConfigData->ChannelInfo.EccType    = ((SpdInfo[235] >> 3) & 0x3);
  if (DdrConfigData->ChannelInfo.EccType != 0){
    DdrConfigData->ChannelInfo.EccType = 1;
  }

  MemoryDeviceInfo->DimmType = SpdInfo[3] & 0xf;;

  Temp = SpdInfo[3] >> 4;
  if(Temp == 0) {
    MemoryDeviceInfo->MemoryTechnology = MemoryTechnologyDram;
  }
  Temp = SpdInfo[7] >> 5 & 0x7;
  DdrConfigData->ChannelInfo.BankGroupNum = Pow (2, Temp);//0-1bg 1-2bg 2-4bg 3-8bg

  Temp = SpdInfo[7] & 0x7;
  DdrConfigData->ChannelInfo.BankNum = 2 * Temp;//0-1b 1-2b 2-4b

  Temp = ((UINT16)(SpdInfo[31] << 8) | (UINT16)(SpdInfo[30]));
  DdrConfigData->ChannelInfo.TAAmin = Temp;
  Temp = ((UINT16)(SpdInfo[33] << 8) | (UINT16)(SpdInfo[32]));
  DdrConfigData->ChannelInfo.TRCDmin = Temp;
  Temp = ((UINT16)(SpdInfo[35] << 8) | (UINT16)(SpdInfo[34]));
  DdrConfigData->ChannelInfo.TRPmin = Temp;
  Temp = ((UINT16)(SpdInfo[37] << 8) | (UINT16)(SpdInfo[36]));
  DdrConfigData->ChannelInfo.TRASmin = Temp;
  Temp = ((UINT16)(SpdInfo[39] << 8) | (UINT16)(SpdInfo[38]));
  DdrConfigData->ChannelInfo.TRCmin = Temp;
  Temp = ((UINT16)(SpdInfo[41] << 8) | (UINT16)(SpdInfo[40]));
  DdrConfigData->ChannelInfo.TWRmin = Temp;
  Temp = ((UINT16)(SpdInfo[43] << 8) | (UINT16)(SpdInfo[42]));
  DdrConfigData->ChannelInfo.TRFC1min = Temp;
  Temp = ((UINT16)(SpdInfo[45] << 8) | (UINT16)(SpdInfo[44]));
  DdrConfigData->ChannelInfo.TRFC2min = Temp;
  Temp = ((UINT16)(SpdInfo[47] << 8) | (UINT16)(SpdInfo[46]));
  DdrConfigData->ChannelInfo.TRFC4RFCsbmin = Temp;
  DdrConfigData->ChannelInfo.ModuleManId = (((UINT16)SpdInfo[512] << 8) + SpdInfo[513]);

  PrintDdrInfo (DimmIndex, DdrConfigData, SmbiosMemoryInfo);

  //Fill Memory smbios information
  //Data Width
  if (DdrConfigData->ChannelInfo.EccType == 1) {
    MemoryDeviceInfo->TotalWidth = 72;
    MemoryDeviceInfo->DataWidth = 64;
  } else {
    MemoryDeviceInfo->TotalWidth = 64;
    MemoryDeviceInfo->DataWidth = 64;
  }

  //Dimm Type
  MemoryDeviceInfo->DimmType = DdrConfigData->ChannelInfo.DimmType;

  //Memory Type
  MemoryDeviceInfo->MemoryType = MemoryTypeDdr5;

  //MaxSpeed
  Temp = SpdInfo[20]  + ((UINT16)SpdInfo[21] << 8);
  Temp = (1000000 / Temp) * 2;
  //eg:4806 -> 4800
  Temp = Temp - (Temp % 10);
  MemoryDeviceInfo->MaxSpeed = Temp;

  //Manufacture
  MemoryDeviceInfo->Manufacturer = DdrConfigData->ChannelInfo.ModuleManId;

  //SN
  SerialNumber = ((UINT32)SpdInfo[512] << 24) + ((UINT32)SpdInfo[518] <<16)
                   + ((UINT32)SpdInfo[519] << 8) + SpdInfo[520];
  MemoryDeviceInfo->SerialNumber = SerialNumber;
  DEBUG ((DEBUG_INFO, "/Serial:0x%x\n", SerialNumber));

  //PN
  CopyMem (MemoryDeviceInfo->PartNumber, &SpdInfo[521], 30);
  MemoryDeviceInfo->PartNumber[30] = '\0';

  //RankNumber
  MemoryDeviceInfo->RankNumber = DdrConfigData->ChannelInfo.RankNum;

  //MemoryTechnology
  MemoryDeviceInfo->MemoryTechnology = MemoryTechnologyDram;

  //ModuleManufacturerID
  Temp = ((UINT16)SpdInfo[512] << 8) + SpdInfo[513];
  MemoryDeviceInfo->ModuleManufacturerID = Temp;
}

/**
  Send an enable signal to power management IC, make it output the appropriate voltage
  to supply power to the memory particles.

  @param[in]  BusAddress   The bus address of PMIC.
  @param[in]  PmicAddress  The slave address of the PMIC.

  @retval  Status  EFI_SUCCESS  Enable PMIC success.
  @retval  Status  EFI_TIMEOUT  Enable PMIC failed.

**/
EFI_STATUS
PmicEnable (
  IN  UINT64  BusAddress,
  IN  UINT8   BusType,
  IN  UINT32  PmicAddress
  )
{
  DEBUG((DEBUG_INFO, "\nEnbale Pmic 0x%x on BUS 0x%x\n", PmicAddress,BusAddress));
  EFI_STATUS    Status;
  UINT8         PmicVrEnable;
  UINT8         Clear;
  //
  //The enable operation requies setting BIT7 to 1 of Register[0x32]
  //
  PmicVrEnable = 0xa0;
  Clear = 0xff;
  Status = EFI_SUCCESS;

  if (BusType == DDR_V3_MCU_BASE_CONFIG_V3_1_I3C) {
    //
    // Init I3C BUS
    //
    I3C_BUS_INFO  Bus;
    ZeroMem (&Bus, sizeof(Bus));
    I3cInit (&Bus, BusAddress, 400, I2C_MODE, XmitBurstStaticSubaddr, SubAddress8Bit);

    //
    // Clear power error
    //
    I3cWrite (&Bus, PmicAddress, 0x14, &Clear, 1);

    Status = I3cWrite (&Bus, PmicAddress, VR_ENABLE, &PmicVrEnable, 1);
    if (Status) {
      DEBUG((DEBUG_INFO, "Pmic 0x%x Enable Fail\n",PmicAddress));
      return Status;
    }
  } else if (BusType == DDR_V3_MCU_BASE_CONFIG_V3_1_I2C) {
      //
      // Init I2C BUS
      //
      I2C_INFO  I2cInfo;
      ZeroMem (&I2cInfo, sizeof(I2cInfo));
      I2cInfo.BusAddress   = BusAddress;
      I2cInfo.SlaveAddress = PmicAddress;
      I2cInfo.Speed        = PcdGet32 (PcdSpdI2cControllerSpeed);

      //
      // Clear power error
      //
      I2cWrite (&I2cInfo, 0x14, 1, &Clear, 1);

      Status = I2cWrite (&I2cInfo, VR_ENABLE, 1, &PmicVrEnable, 1);
      if (Status) {
      DEBUG((DEBUG_INFO, "Pmic 0x%x Enable Fail\n",PmicAddress));
      return Status;
    }
  }

  return EFI_SUCCESS;
}
/**
  Switch the DDR4 SPD page, 0 0r 1.

  @param[in]    BusAddress  I3c bus controller address.
  @param[in]    PageNumber  Page number of spd information, 0 or 1

**/
VOID
I3cSetDdr4Page(
  IN UINTN  BusAddress,
  IN UINT8  PageNumber
  )
{
  I3C_BUS_INFO  I3cBus;
  EFI_STATUS    Status;
  UINT16  PageSetAddress;
  UINT8   PageSet;

  PageSet = 0;

  if (PageNumber == 1){
    PageSetAddress = 0x37;
  } else {
    PageSetAddress = 0x36;
  }

  ZeroMem(&I3cBus, sizeof(I3cBus));
  I3cInit(&I3cBus, BusAddress, 400, I2C_MODE, XmitBurstStaticSubaddr, SubAddress8Bit);
  Status = I3cWrite(&I3cBus, PageSetAddress, 0x0, &PageSet, 1);
  if (EFI_ERROR(Status)) {
    DEBUG((DEBUG_ERROR, "Set page[%d] failed!\n",PageNumber));
  }

}

/**
  Get the parameters of the memory moudle and probe the information
  according to ddr type.DDR4

  @param[in,out]  DdrConfigData     Buffer of ddr information.
  @param[out]     SmbiosMemoryInfo  A pointer to PHYTIUM_MEMORY_SMBIOS_INFO.
  @param[in]      BusAddress        The bus address of ddr,
  @param[in]      SlaveAddress      The slave address of ddr.
  @param[in]      DdrDimmNumber     Index of dimm, such as 0 to max.
  @param[in]      Channel           Channel number of ddr, such as 0 to max, one channel
                                    have two dimm.
  @param[in]      Dimm              Dimm number of ddr, 0 or 1 in a channel.

  @retval  EFI_SUCCESS           Get ddr information successfully.
  @retval  EFI_TIMEOUT           Memory module exist.

**/
EFI_STATUS
I3cDimmProbeDdr4 (
  IN OUT PARAMETER_DDR_CONFIG_V3     *DdrConfigData,
  IN OUT PHYTIUM_MEMORY_SMBIOS_INFO  *SmbiosMemoryInfo,
  IN     UINT64                      BusAddress,
  IN     UINT32                      SlaveAddress,
  IN     UINT8                       DdrDimmNumber,
  IN     UINT8                       Channel,
  IN     UINT8                       Dimm
  )
{
  I3C_BUS_INFO  Bus;
  EFI_STATUS    Status;
  UINT8  Buffer[512];
  UINT32 Index;

  ZeroMem (Buffer, sizeof (Buffer));
  ZeroMem(&Bus, sizeof(Bus));
  I3cInit(&Bus, BusAddress, 400, I2C_MODE, XmitBurstStaticSubaddr, SubAddress8Bit);
  DEBUG((DEBUG_INFO, "BusAddress is 0x%llx, SlaveAddress is 0x%x\n",BusAddress, SlaveAddress));
  //
  // Initialize I2C Bus which contain SPD
  //
  for (Index = 0; Index < 4; Index++) {
    Status = I3cRead(&Bus, SlaveAddress, Index * 64, &Buffer[Index * 64], 64);
    if (Status != 0) {
      DEBUG((DEBUG_ERROR, "Read I3C DDR4 Page0 SPD[0-256] Failed\n"));
      return EFI_NOT_FOUND;
    }
  }
  //
  // Set Page1
  //
  I3cSetDdr4Page(BusAddress, 1);
  for (Index = 0; Index < 4; Index++) {
    Status = I3cRead(&Bus, SlaveAddress, Index * 64, &Buffer[(Index * 64) + 256], 64);
    if (Status != 0){
      DEBUG((DEBUG_ERROR, "Read I3C DDR4 Page1 SPD[0-256] Failed\n"));
      return EFI_NOT_FOUND;
    }
  }
  I3cSetDdr4Page(BusAddress, 0);

  ParseDDR4Spd (DdrDimmNumber, Buffer, DdrConfigData, SmbiosMemoryInfo);

  return EFI_SUCCESS;
}

/**
  Get the parameters of the memory moudle and probe the information
  according to ddr type.DDR5

  @param[in,out]  DdrConfigData     Buffer of ddr information.
  @param[out]     SmbiosMemoryInfo  A pointer to PHYTIUM_MEMORY_SMBIOS_INFO.
  @param[in]      BusAddress        The bus address of ddr,
  @param[in]      SlaveAddress      The slave address of ddr.
  @param[in]      DdrDimmNumber     Index of dimm, such as 0 to max.
  @param[in]      Channel           Channel number of ddr, such as 0 to max, one channel
                                    have two dimm.
  @param[in]      Dimm              Dimm number of ddr, 0 or 1 in a channel.

  @retval  EFI_SUCCESS           Get ddr information successfully.
  @retval  EFI_TIMEOUT           Memory module exist.

**/
EFI_STATUS
I2cDimmProbeDdr5 (
  IN OUT PARAMETER_DDR_CONFIG_V3  *DdrConfigData,
  IN OUT PHYTIUM_MEMORY_SMBIOS_INFO  *SmbiosMemoryInfo,
  IN UINT64                       BusAddress,
  IN UINT32                       SlaveAddress,
  IN  UINT8                       DdrDimmNumber,
  IN  UINT8                       Channel,
  IN  UINT8                       Dimm
  )
{
  I2C_INFO    I2cInfo;
  EFI_STATUS  Status;
  UINT16      NvmSubAddress;
  UINT8       Buffer[SPD_BYTE_NUMBER_DDR5];
  UINT8       PageNumber;

  I2cInfo.BusAddress   = BusAddress;
  I2cInfo.SlaveAddress = SlaveAddress;
  I2cInfo.Speed        = PcdGet32 (PcdSpdI2cControllerSpeed);
  ZeroMem (Buffer, sizeof (Buffer));
  DEBUG ((DEBUG_INFO, "Bus:0x%x, SlaveAddress:0x%x\n", BusAddress, SlaveAddress));

  I2cInit(&I2cInfo);
  NvmSubAddress = 0x80;

  //
  //Read 5 pages information
  //
  for (PageNumber = 0; PageNumber < 5; PageNumber++) {
    //
    //Set Page
    //
    Status = I2cWrite (&I2cInfo, 0xb, 1, &PageNumber, 1);//0xb is SPDHub MR11 register
    if (EFI_ERROR (Status)){
      goto Out;
    }

    //
    //Read SpdInformation from hub
    //
    Status = I2cRead (&I2cInfo, NvmSubAddress, 1, &Buffer[PageNumber * BYTES_EVERY_PAGE_DDR5], BYTES_EVERY_PAGE_DDR5);
    if (EFI_ERROR (Status)){
      goto Out;
    }
  }

  ParseDDR5Spd (DdrDimmNumber, Buffer, DdrConfigData, SmbiosMemoryInfo);

Out:
  return Status;
}

/**
  Get the parameters of the memory moudle and probe the information
  according to ddr type.DDR4

  @param[in,out]  DdrConfigData     Buffer of ddr information.
  @param[out]     SmbiosMemoryInfo  A pointer to PHYTIUM_MEMORY_SMBIOS_INFO.
  @param[in]      BusAddress        The bus address of ddr,
  @param[in]      SlaveAddress      The slave address of ddr.
  @param[in]      DdrDimmNumber     Index of dimm, such as 0 to max.
  @param[in]      Channel           Channel number of ddr, such as 0 to max, one channel
                                    have two dimm.
  @param[in]      Dimm              Dimm number of ddr, 0 or 1 in a channel.

  @retval  EFI_SUCCESS           Get ddr information successfully.
  @retval  EFI_TIMEOUT           Memory module exist.

**/
EFI_STATUS
I2cDimmProbeDdr4 (
  IN OUT PARAMETER_DDR_CONFIG_V3    *DdrConfigData,
  IN OUT PHYTIUM_MEMORY_SMBIOS_INFO *SmbiosMemoryInfo,
  IN UINT64                         BusAddress,
  IN UINT32                         SlaveAddress,
  IN  UINT8                         DdrDimmNumber,
  IN  UINT8                         Channel,
  IN  UINT8                         Dimm
  )
{
  UINT8     Buffer[SPD_BYTE_NUMBER_DDR4];
  UINT8     Temp;
  I2C_INFO  I2cInfo;

  ZeroMem (Buffer, sizeof (Buffer));
  ZeroMem (&I2cInfo, sizeof (I2C_INFO));

  I2cInfo.SlaveAddress = SlaveAddress;
  I2cInfo.BusAddress   = BusAddress;
  I2cInfo.Speed        = PcdGet32 (PcdSpdI2cControllerSpeed);
  DEBUG ((DEBUG_INFO, "I2CBase:%x, I2CSpeed:%x, I2CSlaveAddress:%x\n",I2cInfo.BusAddress, I2cInfo.Speed, I2cInfo.SlaveAddress));

  //
  // Initialize I2C Bus which contain SPD
  //
  I2cInit(&I2cInfo);
  Temp = I2cRead (&I2cInfo, 0, 1, Buffer, BYTES_EVERY_PAGE_DDR4);
  if (Temp != 0) {
    DEBUG((DEBUG_ERROR, "Read I2C Failed\n"));
    return EFI_NOT_FOUND;
  }
  if (Dimm == 0) {
    SpdSetpage (&I2cInfo, 1);
    I2cRead (&I2cInfo, 0, 1, &Buffer[BYTES_EVERY_PAGE_DDR4], BYTES_EVERY_PAGE_DDR4);
    SpdSetpage (&I2cInfo, 0);
    ParseDDR4Spd (Channel, Buffer, DdrConfigData, SmbiosMemoryInfo);
  }

  return EFI_SUCCESS;
}

/**
  Get the parameters of the memory moudle and probe the information
  according to ddr type.DDR5

  @param[in,out]  DdrConfigData     Buffer of ddr information.
  @param[out]     SmbiosMemoryInfo  A pointer to PHYTIUM_MEMORY_SMBIOS_INFO.
  @param[in]      BusAddress        The bus address of ddr,
  @param[in]      SlaveAddress      The slave address of ddr.
  @param[in]      DdrDimmNumber     Index of dimm, such as 0 to max.
  @param[in]      Channel           Channel number of ddr, such as 0 to max, one channel
                                    have two dimm.
  @param[in]      Dimm              Dimm number of ddr, 0 or 1 in a channel.

  @retval  EFI_SUCCESS           Get ddr information successfully.
  @retval  EFI_TIMEOUT           Memory module exist.

**/
EFI_STATUS
I3cDimmProbeDdr5 (
  IN OUT PARAMETER_DDR_CONFIG_V3  *DdrConfigData,
  IN OUT PHYTIUM_MEMORY_SMBIOS_INFO  *SmbiosMemoryInfo,
  IN UINT64                       BusAddress,
  IN UINT32                       SlaveAddress,
  IN  UINT8                       DdrDimmNumber,
  IN  UINT8                       Channel,
  IN  UINT8                       Dimm
  )
{
  I3C_BUS_INFO  Bus;
  EFI_STATUS    Status;
  UINT16        NvmSubAddress;
  UINT8         Buffer[SPD_BYTE_NUMBER_DDR5];
  UINT8         PageNumber;

  NvmSubAddress = 0x80;
  ZeroMem (Buffer, sizeof (Buffer));
  DEBUG ((DEBUG_INFO, "Bus:0x%llx, SlaveAddress:0x%x\n", BusAddress, SlaveAddress));

  ZeroMem(&Bus, sizeof(Bus));
  I3cInit(&Bus, BusAddress, 400, I2C_MODE, XmitBurstStaticSubaddr, SubAddress8Bit);
  //
  //Read 5 pages information
  //
  for (PageNumber = 0; PageNumber < 5; PageNumber++) {
    //
    //Set Page
    //
    Status = I3cWrite (&Bus, SlaveAddress, 0xb, &PageNumber, 1);//0xb is SPDHub MR11 register
    if (EFI_ERROR (Status)){
      DEBUG ((DEBUG_ERROR,"Set Page%d Error\n", PageNumber));
      goto Out;
    }

    //
    //Read SpdInformation from hub
    //
    Status = I3cRead (&Bus, SlaveAddress, NvmSubAddress, &Buffer[PageNumber * BYTES_EVERY_PAGE_DDR5], 64);
    Status = I3cRead (&Bus, SlaveAddress, NvmSubAddress + 64, &Buffer[PageNumber * BYTES_EVERY_PAGE_DDR5 + 64], 64);
    if (EFI_ERROR (Status)){
      DEBUG ((DEBUG_ERROR, "Read SpdHub 0x%x Error\n", SlaveAddress));
      goto Out;
    }
  }

  ParseDDR5Spd (DdrDimmNumber, Buffer, DdrConfigData, SmbiosMemoryInfo);

Out:
  return Status;
}

/**
  Probe memory module.

  @param[in,out]  DdrConfigData    Buffer of ddr information.
  @param[out]     SmbiosMemoryInfo A pointer to PHYTIUM_MEMORY_SMBIOS_INFO.
  @param[in]      BusAddress       The bus address of ddr,
  @param[in]      DdrSlaveAddress  The slave address of ddr.
  @param[in]      DdrDimmNumber    Index of dimm, such as 0 to max.
  @param[in]      Channel          Channel number of ddr, such as 0 to max, one channel
                                   have two dimm.
  @param[in]      Dimm             Dimm number of ddr, 0 or 1 in a channel.
  @param[in]      BusType          Which bus type of the memory connection,i2c(1) or i3c(0).
  @param[in]      DdrTpye          Which type of the dimm,ddr4(1) or ddr5(0).

  @retval  EFI_SUCCESS             Get ddr information successfully.
  @retval  EFI_TIMEOUT             Memory module exist.

**/
EFI_STATUS
GetDdrSpdParameter (
  IN OUT PARAMETER_DDR_CONFIG_V3  *DdrConfigData,
  IN  PHYTIUM_MEMORY_SMBIOS_INFO  *SmbiosMemoryInfo,
  IN  UINT64                      BusAddress,
  IN  UINT32                      DdrSlaveAddress,
  IN  UINT8                       DdrDimmNumber,
  IN  UINT8                       Channel,
  IN  UINT8                       Dimm,
  IN  UINT8                       BusType,
  IN  UINT8                       DdrTpye
  )
{
  EFI_STATUS Status;

  Status = EFI_SUCCESS;

  if (BusType == DDR_V3_MCU_BASE_CONFIG_V3_1_I3C){

    if (DdrTpye == DDR_V3_MCU_BASE_CONFIG_V3_1_DDR4) {
      Status = I3cDimmProbeDdr4 (DdrConfigData, SmbiosMemoryInfo, BusAddress, DdrSlaveAddress, DdrDimmNumber, Channel, Dimm);
    } else {
      Status = I3cDimmProbeDdr5 (DdrConfigData, SmbiosMemoryInfo, BusAddress, DdrSlaveAddress, DdrDimmNumber, Channel, Dimm);
    }
    if (EFI_ERROR (Status)){
      return Status;
    }

  } else if (BusType == DDR_V3_MCU_BASE_CONFIG_V3_1_I2C){

      if (DdrTpye == DDR_V3_MCU_BASE_CONFIG_V3_1_DDR4) {
        Status = I2cDimmProbeDdr4 (DdrConfigData, SmbiosMemoryInfo, BusAddress, DdrSlaveAddress, DdrDimmNumber, Channel, Dimm);
      } else {
        Status = I2cDimmProbeDdr5 (DdrConfigData, SmbiosMemoryInfo, BusAddress, DdrSlaveAddress, DdrDimmNumber, Channel, Dimm);
      }
      if (EFI_ERROR (Status)){
        return Status;
      }
  }

  return Status;
}

/**
  Get DDR config  parameter.

  @param[in,out]  DdrConfigData  configuration information.

**/
VOID
GetDdrConfigParameter (
  IN OUT PARAMETER_DDR_CONFIG_V3  *DdrConfigData
  )
{
  ARM_SMC_ARGS  ArmSmcArgs;
  EFI_STATUS  Status;
  UINT8       BusType;
  UINT8       DdrType;
  UINT8       DieIndex;
  UINT8       ChannelIndex;
  UINT8       DimmIndex;
  UINT8       DieCount;
  UINT8       ChannelCount;
  UINTN       DimmCount;
  UINT8       DIMMIndex;
  UINT8       DieOffet;
  UINT8       DevicesCount;
  UINT8      *DdrDimmNumber;
  UINT8      *DdrSpdHubAddress;
  UINT8      *DdrPmicAddress;
  UINT8       ForceSpd;
  UINT64      BusAddress;
  UINT64      Bus;
  UINT8       MaxDimmCount;
  UINT64      Size;
  UINT64      CpuType;
  PHYTIUM_MEMORY_SMBIOS_INFO SmbiosMemoryInfo;

  ZeroMem (&SmbiosMemoryInfo, sizeof (PHYTIUM_MEMORY_SMBIOS_INFO));
  GetDieNumFromParTable(&DieCount);
  BusType          = DdrConfigData->McuConfig1.Bits.SpdBusType;
  DdrType          = DdrConfigData->McuConfig1.Bits.DdrType;

  //
  // Get Cpu Type
  //
  ZeroMem (&ArmSmcArgs, sizeof (ARM_SMC_ARGS));
  ArmSmcArgs.Arg0 = 0x80000002;
  ArmSmcArgs.Arg1 = 0x0;
  ArmCallSmc (&ArmSmcArgs);
  CpuType = ArmSmcArgs.Arg0;

  DimmCount        = PcdGet8 (PcdDdrDimmCount);
  ChannelCount     = PcdGet8 (PcdDdrChannelCount);
  DieOffet         = PcdGet64 (PcdDieOffset);

  switch (CpuType) {
  case CPU_ID_32C:
    DdrSpdHubAddress = PcdGetPtr (PcdDdrSpdHubAddressArray32C);
    DdrPmicAddress   = PcdGetPtr (PcdDdrPmicAddressArray32C);
    DevicesCount     = PcdGetSize (PcdDdrSpdHubAddressArray32C);
    DdrDimmNumber    = PcdGetPtr (PcdDdrDimmNumberArray32C);
    break;

  case CPU_ID_16C:
    DdrSpdHubAddress = PcdGetPtr (PcdDdrSpdHubAddressArray16C);
    DdrPmicAddress   = PcdGetPtr (PcdDdrPmicAddressArray16C);
    DevicesCount     = PcdGetSize (PcdDdrSpdHubAddressArray16C);
    DdrDimmNumber    = PcdGetPtr (PcdDdrDimmNumberArray16C);
    break;

  case CPU_ID_64C:
  default:
    DdrSpdHubAddress = PcdGetPtr (PcdDdrSpdHubAddressArray64C);
    DdrPmicAddress   = PcdGetPtr (PcdDdrPmicAddressArray64C);
    DevicesCount     = PcdGetSize (PcdDdrSpdHubAddressArray64C);
    DdrDimmNumber    = PcdGetPtr (PcdDdrDimmNumberArray64C);
    break;
  }

  MaxDimmCount = DieCount * ChannelCount * DimmCount;
  SmbiosMemoryInfo.MaxDimmCount = MaxDimmCount;
  if (BusType == DDR_V3_MCU_BASE_CONFIG_V3_1_I2C) {
    BusAddress   = PcdGet64 (PcdSpdMatserI2cBaseAddress);
  } else {
      BusAddress = PcdGet64 (PcdSpdMatserI3cBaseAddress);
  }

  DIMMIndex = 0;
  DimmIndex = 0;
  ForceSpd = (UINT8) (DdrConfigData->McuConfig.Bits.SpdForce);

  DEBUG ((DEBUG_INFO, "DieCount %d\n", DieCount));
  DEBUG ((DEBUG_INFO, "ChannelCount %d\n", ChannelCount));
  DEBUG ((DEBUG_INFO, "DimmCount %d\n", DimmCount));
  DEBUG ((DEBUG_INFO, "DieOffset is %d\n",DieOffet));

  PostCode (POST_CODE_MEM_INIT_DETECTION, 0, 0);

  if (ForceSpd == 1) {
    DEBUG ((DEBUG_INFO, "Read Parameter form Parameter Table\n"));
    PrintDdrInfo (DimmIndex, DdrConfigData, &SmbiosMemoryInfo);
  } else {
    for (DieIndex = 0; DieIndex < DieCount; DieIndex++) {
      for (ChannelIndex = 0; ChannelIndex < ChannelCount; ChannelIndex++) {
        DEBUG ((DEBUG_INFO, "Channel Enable : %x\n", DdrConfigData->ChEnableMap));
        if (!((DdrConfigData->ChEnableMap >> DIMMIndex) & 0x1)) {
          DIMMIndex++;
          continue;
        }
        for (DimmIndex = 0; DimmIndex < DimmCount; DimmIndex ++) {
          PostCode (POST_CODE_MEM_INIT_SPD_READ, DieIndex, ChannelIndex);
          DEBUG ((DEBUG_INFO, "Die[%d] Channel[%d] Dimm[%d]\n", DieIndex, ChannelIndex, DimmIndex));
          DEBUG ((DEBUG_INFO, "DIMMIndex[%d]\n", DIMMIndex));
          Bus =((UINT64) DieIndex << DieOffet) | BusAddress;
          Status = GetDdrSpdParameter (
                     DdrConfigData,
                     &SmbiosMemoryInfo,
                     Bus,
                     DdrSpdHubAddress[DIMMIndex],
                     DdrDimmNumber[DIMMIndex],
                     ChannelIndex,
                     DimmIndex,
                     BusType,
                     DdrType
                     );
          if (EFI_ERROR (Status)) {
            PostCode (POST_CODE_MEM_INIT_SPD_READ_FAILED, DieIndex, ChannelIndex);
            DevicesCount--;
            DEBUG ((DEBUG_ERROR, "Die[%d] Channel[%d] Dimm[%d] Don't Probe\n\n", DieIndex, ChannelIndex, DimmIndex));
            DdrConfigData->ChEnableMap &= ~(0x1 << DIMMIndex);
          } else {
             DdrConfigData->ChEnableMap |= (0x1 << DIMMIndex);
             if (DdrType == DDR_V3_MCU_BASE_CONFIG_V3_1_DDR5) {
               PmicEnable (Bus, BusType, DdrPmicAddress[DIMMIndex]);
             }
          }
          DIMMIndex++;
        }
      }
    }
  }
  /* Creat DDR SpdInfo Hob */
  Size = sizeof(PHYTIUM_MEMORY_SMBIOS_INFO);
  BuildGuidDataHob (&gPlatformMemorySmbiosInfoGuid, &SmbiosMemoryInfo, Size);
  DEBUG ((DEBUG_INFO, "DDR tranning map is 0x%x\n",DdrConfigData->ChEnableMap));

  if (DevicesCount == 0) {
    PostCode (POST_CODE_MEM_INIT_DETECTION_NONE, 0, 0);
    DEBUG ((DEBUG_ERROR, "Don't Find Memory Devices\n"));
  }
}
